#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/cdev.h>
#include <linux/mm.h>
#define DEVICE_NAME "led"
#define LED_MAJOR 254
static int led_major = LED_MAJOR;
struct mini_led_dev
{
    struct cdev cdev;
};             //自定义设备结构体
struct mini_led_dev *devp;
static unsigned long mini_led_table[] = {
    S3C2416_GPG0,
    S3C2416_GPG1,
    S3C2416_GPG2,
    S3C2416_GPG3,
};
static unsigned long mini_led_cfg_table[] = {
    S3C2416_GPG0_OUTP,
    S3C2416_GPG1_OUTP,
    S3C2416_GPG2_OUTP,
    S3C2416_GPG3_OUTP,
};

int led_on(int arg)
{
    s3c2416_gpio_setpin(mini_led_table[arg], 0);
    return 0;
}
int led_off(int arg)
{
    s3c2416_gpio_setpin(mini_led_table[arg], 1);
    return 0;
}
static int mini_led_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg)
{
    switch(cmd)
        {
        case 0:
            led_off(arg);
            break;
        case 1:
            led_on(arg);
            break;
        default:
            return -1; 
        }
}
static int mini_led_open(struct inode *inode, struct file *filp)
{
    struct mini_led_dev *devp;
    devp = container_of(inode->i_cdev, struct mini_led_dev,cdev);
    filp->private_data = devp;
    return 0;
}
static int mini_led_release(struct inode *inode, struct file *filp)
{
    return 0;
}
static struct file_operations mini_led_fops = {
    .owner = THIS_MODULE,
    .ioctl = mini_led_ioctl,
    .open = mini_led_open,
    .release = mini_led_release,
};
static void led_setup_cdev(struct mini_led_dev *dev, int index)
{
    int err;
    int devno = MKDEV(led_major, index);
    cdev_init(&dev->cdev, &mini_led_fops);
    dev->cdev.owner = THIS_MODULE;
    dev->cdev.ops = &mini_led_fops;
    err = cdev_add(&dev->cdev, devno, 1);
    if (err)
    {
        printk(KERN_NOTICE "Error %d adding led %d\n", err, index);
    }
}

/*模块加载*/
static __int int mini_led_init(void)       
{
    int result;
    int i;
    printk(KERN_ALERT "Hello, world\n");
    dev_t devno = MKDEV(led_major, 0);
    if (led_major)
    {
        result = register_chrdev_region(devno, 0, DEVICE_NAME); 
    }
    else
    {
        result = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME);
    }
    if (result < 0)
    { 
        return result;
    }  
    devp = kmalloc(sizeof(struct mini_led_dev), GFP_KERNEL);
    if (! devp)
    {
        result = -1;
        goto fail_malloc;
    }
    for (i = 0; i < 4; i++)
    {
        s3c2416_gpio_cfgpin(mini_led_table[i], mini_led_cfg_table[i]);
        s3c2416_gpio_setpin(mini_led_table[i], 1);
    }
    memset(devp, 0, sizeof(struct mini_led_dev));
    led_setup_cdev(devp, 0);
    return 0; 
    fail_malloc:
    unregister_chrdev_region(devno, 1);
    return result;
}

/*模块卸载*/
void mini_led_exit(void)
{
    printk(KERN_ALERT "Goodbye, world\n");
    cdev_del(&devp->cdev);
    kfree(devp);
    unregister_chrdev_region(MKDEV(led_major, 0), 1);
}
MODULE_AUTHOR("xkq");
MODULE_LICENSE("Dual BSD/GPL");
module_init(mini_led_init);
module_exit(mini_led_exit);